/* C std includes */
#include <stdio.h>
#include <string.h>

/* lua */
#include <lua5.1/lua.h>
#include <lua5.1/lauxlib.h>
#include <lua5.1/lualib.h>

/* amazo includes */
#include "include/cli.h"
#include "include/display.h"
#include "include/logger.h"
#include "include/dynamic_list.h"

#define CLI_LINES 5
#define ANIM_STEP 1

/* Bitmap holding the whole console */
BITMAP * cli_buffer = NULL;
extern lua_State *lua;

/* Current input */
dynlist_t * cur_input = NULL;

/* Current line and line height */
int padding = 1;
int offx = 0, offy = 0;
int lh = 0;

void am_init_cli()
{
	offx = offy = padding;
	lh = padding*2 + text_height(font);
	cur_input = dynlist_create("CLI Input Buffer", free);
	cli_buffer = create_bitmap(canvas->w, canvas->h);
	rectfill(cli_buffer, 0, 0, cli_buffer->w, cli_buffer->h, makecol(0, 0, 0));
	textout_ex(cli_buffer, font, "CLI> ", offx, offy, makecol(255, 255, 255), -1);
	offx += text_length(font, "CLI> ");
}

/*
 * Scroll the CLI by one line 
 */
void am_scroll_cli()
{
	/* Move the visuals up a line */
	blit(cli_buffer, cli_buffer, 0, lh, 0, 0, cli_buffer->w, cli_buffer->h - lh);
	rectfill(cli_buffer, 0, cli_buffer->h - lh, cli_buffer->w, cli_buffer->h, makecol(0, 0, 0)); 
}

void am_term_cli()
{
	dynlist_free(&cur_input);
	destroy_bitmap(cli_buffer);
}

void am_draw_cli(int clih, int height)
{
	rectfill(canvas, 0, canvas->h-height-10, canvas->w, canvas->h-height-5, makecol(0, 0, 0));
	rectfill(canvas, 0, canvas->h-5, canvas->w, canvas->h, makecol(0, 0, 0));
	blit(	cli_buffer, canvas, 
			0, (clih > offy ? 0 : offy-clih-padding+lh), 	
			0, canvas->h - height - 5, 									
			cli_buffer->w, height); 	
}

void am_show_cli()
{
	int clih = CLI_LINES * lh;
	BITMAP * backup = create_bitmap(canvas->w, clih+10);
	
	/* Make a backup image of what's under the CLI */
	blit(canvas, backup, 0, canvas->h-backup->h, 0, 0, backup->w, backup->h);
	
	/* Slide in the CLI */			
	for(int I = 1; I <= clih; I += ANIM_STEP)
	{
			am_draw_cli(clih, I);					
			sync();
			rest(3);
	}
	am_draw_cli(clih, clih);
	sync();
	
	/* Allow to input a command */
	int 	done = 0;
	while(!done)
	{
		/* Exit the CLI with ESC */
		if(key[KEY_TAB]) {
			key[KEY_TAB] = 0;
			done = 1;
		}
		
		/* Update the buffer */
		if(keypressed())
      	{
			int  newkey   = readkey();
         	char ASCII    = newkey & 0xff;
         	char scancode = newkey >> 8;

         	if(ASCII >= 32 && ASCII <= 126)
         	{
         		/* a character key was pressed; add it to the stack */
         		char * mem = malloc(2);
         		mem[0] = ASCII;
				mem[1] = '\0';
				dynlist_push(cur_input, mem);
				
				/* Print the char */ 
				int tw = text_length(font, mem);
				if(offx + tw > cli_buffer->w)
      			{
      				/* We're crossing the right boundary, see if we can 
      			 	 * move down or if we have to scroll */
      				if(offy + 2*lh > cli_buffer->h)
      					am_scroll_cli();
      				else
      					offy += lh;
      				
      				offx = padding;
      			}
      			textout_ex(cli_buffer, font, mem, offx, offy, makecol(255, 255, 255), -1);	 
      			offx += tw;
         	}
         	else if(scancode == KEY_ENTER)
         	{
         		/* Assemble the command from the stacked chars */
         		char * command = malloc(cur_input->count + 1);
         		command[cur_input->count] = '\0';
         		for(int I = 0; I < cur_input->count; I++)
         		{
         			char * m = dynlist_get(cur_input, I);
         			memcpy(command + I, m, 1);
         		}
         		
         		/* Now get rid of the stack */
         		for(int I = cur_input->count-1; I >= 0; I--)
         			dynlist_remove(cur_input, I);
         			
         		luaL_dostring(lua, command);
         		
         		offx = padding + text_length(font, "CLI> ");
         		if(offy + 2*lh > cli_buffer->h)
      				am_scroll_cli();
      			else
      				offy += lh;
         		
         		textout_ex(cli_buffer, font, "CLI> ", padding, offy, makecol(255, 255, 255), -1);
         		
         	}
         	else if(scancode == KEY_BACKSPACE && cur_input->count > 0)
         	{
         		/* Free the last entered character */
            	void * mem = dynlist_pop(cur_input);
            	if(mem != NULL) {
            		/* Delete it from the CLI */
            		int tw = text_length(font, mem);
            		offx -= tw;
         	   		rectfill(cli_buffer, offx, offy, offx+tw, offy+lh, makecol(0, 0, 0));
         			
         			/* Free memory */
            		free(mem);
         		}
         	}
      		
      		/* Draw the CLI */
      		am_draw_cli(clih, clih); 
			sync();
      	}
	}
	
	/* Slide out the CLI */
	for(int I = clih; I >= 0; I -= ANIM_STEP)
	{
			blit(	backup, canvas, 					
					0, 0, 	
					0, canvas->h - backup->h, 									
					backup->w, backup->h); 
			blit(	cli_buffer, canvas, 					
					0, (clih > offy ? 0 : offy-clih-padding+lh), 	
					0, canvas->h - I, 									
					cli_buffer->w, I); 						
			sync();
			rest(3);
	}
	blit(	backup, canvas, 					
					0, 0, 	
					0, canvas->h - backup->h, 									
					backup->w, backup->h); 
	sync();
	
	destroy_bitmap(backup);
}
